package io.gitee.twoke.generator.engine.impl.cmd;

import io.gitee.twoke.generator.engine.impl.interceptor.Command;
import io.gitee.twoke.generator.engine.impl.interceptor.CommandContext;
import io.gitee.twoke.generator.engine.impl.metadata.Table;
import io.gitee.twoke.generator.engine.impl.metadata.TableColumn;
import io.gitee.twoke.generator.engine.impl.persistence.entity.DataTableColumnEntity;
import io.gitee.twoke.generator.engine.impl.persistence.entity.DataTableColumnEntityManager;
import io.gitee.twoke.generator.engine.impl.persistence.entity.DataTableEntity;
import io.gitee.twoke.generator.engine.impl.persistence.entity.DataTableEntityManager;
import cn.hutool.core.collection.CollectionUtil;
import cn.hutool.core.util.StrUtil;
import org.apache.commons.lang3.StringUtils;
import org.apache.ibatis.type.JdbcType;

import java.math.BigDecimal;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.util.List;

public class ImportTableCmd implements Command<Void> {

    private String tableName;
    private String tableId;
    private Table table;
    private List<TableColumn> columns;
    private boolean isLoadDatabase;

    public ImportTableCmd(String tableName) {
        if (StrUtil.isBlank(tableName)) throw new IllegalArgumentException("table name is empty");
         this.tableName = tableName;
         this.isLoadDatabase = true;
    }

    public ImportTableCmd(Table table, List<TableColumn> columns) {
        if (StrUtil.isBlank(table.getName())) throw new IllegalArgumentException("table name is empty");
        if (CollectionUtil.isEmpty(columns)) throw new IllegalArgumentException("table columns is empty");
        this.table = table;
        this.tableName = table.getName();
        this.columns = columns;
        this.isLoadDatabase = false;
    }


    @Override
    public Void execute(CommandContext commandContext) {
        long countByTableName = commandContext.getDataTableEntityManager().findDataTableCountByTableName(tableName);
        if (countByTableName > 0) {
            throw new IllegalArgumentException("table '" + tableName + "' already exists");
        }
        if (isLoadDatabase) {
            loadMetaData(commandContext);
        }
        if (this.table == null || this.columns == null || this.columns.size() == 0) {
            throw new IllegalArgumentException("table name or columns is empty");
        }
        saveTable(commandContext);
        saveColumns(commandContext);
        return null;
    }

    private void loadMetaData(CommandContext commandContext) {
        // 表信息
        this.table = commandContext.getMetaDataService().createTableQuery().tableName(tableName).singleResult();
        this.columns = commandContext.getMetaDataService().createTableColumQuery().tableName(tableName).list();
    }

    private void saveTable(CommandContext commandContext) {
        if (table == null) throw new RuntimeException("table '"+tableName+"' not found");
        DataTableEntityManager entityManager = commandContext.getDataTableEntityManager();
        DataTableEntity dataTableEntity = entityManager.create();
        dataTableEntity.setTableName(table.getName());
        dataTableEntity.setTableComment(table.getComment());
        dataTableEntity.setAuthor("[默认值]");
        dataTableEntity.setSubTableName("[默认值]");
        dataTableEntity.setSubTableFkName("[默认值]");
        dataTableEntity.setClassName(StringUtils.capitalize(table.getName()));
        entityManager.insert(dataTableEntity);
        this.tableId = dataTableEntity.getId();
    }

    private void saveColumns(CommandContext commandContext) {
        DataTableColumnEntityManager entityManager = commandContext.getDataTableColumnEntityManager();
        int index = 0;
        for (TableColumn column : columns) {
            DataTableColumnEntity dataTableColumnEntity = entityManager.create();
            dataTableColumnEntity.setTableId(tableId);
            dataTableColumnEntity.setColumnName(column.getName());
            dataTableColumnEntity.setColumnComment(column.getComment());
            dataTableColumnEntity.setJdbcType(column.getJdbcType().name());
            String capitalize = StringUtils.capitalize(column.getName());
            dataTableColumnEntity.setJavaProperty(capitalize.substring(0, 1).toLowerCase() + capitalize.substring(1));
            dataTableColumnEntity.setJavaType(jdbcTypeToJavaType(column.getJdbcType()).getSimpleName());
            dataTableColumnEntity.setIsRequired(column.isRequired());
            dataTableColumnEntity.setIsPrimaryKey(column.isPrimaryKey());
            dataTableColumnEntity.setSort(index);
            commandContext.getDataTableColumnEntityManager()
                    .insert(dataTableColumnEntity);
            index++;
        }
    }

    public static Class<?> jdbcTypeToJavaType(JdbcType jdbcType) {
        switch (jdbcType) {
            case CHAR:
            case VARCHAR:
            case LONGVARCHAR:
            case NCHAR:
            case NVARCHAR:
            case LONGNVARCHAR:
            case SQLXML:
                return String.class;
            case NUMERIC:
            case DECIMAL:
                return BigDecimal.class;
            case BIT:
                return Boolean.class;
            case TINYINT:
                return Byte.class;
            case SMALLINT:
                return Short.class;
            case INTEGER:
                return Integer.class;
            case BIGINT:
                return Long.class;
            case REAL:
            case FLOAT:
                return Float.class;
            case DOUBLE:
                return Double.class;
            case BINARY:
            case VARBINARY:
            case LONGVARBINARY:
            case BLOB:
                return byte[].class;
            case DATE:
                return LocalDate.class;
            case TIME:
            case TIMESTAMP:
                return LocalDateTime.class;
            case CLOB:
                return java.sql.Clob.class;
            case REF:
                return java.sql.Ref.class;
            case STRUCT:
                return java.sql.Struct.class;
            case JAVA_OBJECT:
            case DISTINCT:
            case OTHER:
                return Object.class;
            case CURSOR:
                return java.sql.ResultSet.class;
            case UNDEFINED:
            case NULL:
            default:
                return null;
        }
    }
}
